// *** ADDED BY HEADER FIXUP ***
#include <wx/textfile.h>
// *** END ***
// *** ADDED BY HEADER FIXUP ***
#include <wx/string.h>
// *** END ***
#include "LinkThread.h"
#include <cstring>
#include <string>
#include "FormatString.h"
#include <wx/xml/xml.h>
#include <wx/sstream.h>
#include <wx/sckstrm.h>
#include "MyException.h"
#include "Mysocket.h"
/** \page illustrate 交互协议（XML标准）
 * 1.<Setting>  ---设置信息 \n
 * \li <AddQueryType> --添加咨询类型 \n
 * \li <RemoveQueryType> \n
 *
 * 2.<Message>信息内容</Message> --发送消息 \n
 * \li <Poster>  ---发送者
 * \li <Time> --发送时间
 * \li <Content> --消息内容
 *
 * 3.<Exit>退出原因</Exit> \n
 * 4.<Respond>  --用于收集语料库 \n
 * \li <QueryType>  --询问类型
 * \li <Query>  --询问
 * \li <That>
 * \li <Answer> --回答
 *
 * 5.<Login>验证信息</Login>
 *
 * 6.<Ack>验证信息</Ack>
 */

/** \brief 对于每个链接产生一个线程处理
 *
 */
LinkThread::LinkThread(Kernel *kernel,wxSocketBase* sock,LoginSet* log,PhoneAimlManager *pm):
    kernel(kernel),sock(sock),loger(log),pm(pm),timeStamp(0)
{
    botName = wxString(kernel->respond("BOT NAME", "system").c_str(),wxConvUTF8);
    //sock->SetFlags(wxSOCKET_WAITALL);
    sock->GetPeer(addr);
    sock->SetFlags(wxSOCKET_WAITALL);
    Create();
    Run();
}
LinkThread::~LinkThread()
{
    if(!userName.empty())
    {
        loger->LogOut(userName);
        pm->SaveData();
    }
    sock->Destroy();
}

/**
 * \brief 线程入口
 *
 * 服务端处理流程：\n
 * 1.接收登录信息\n
 * 2.验证名字是否已登录，是发送<exit>后退出，否发送认证信息\n
 * 3.正式开始交互，发送问候信息。
 */
void* LinkThread::Entry()
{
    wxXmlDocument doc;
    try/**< 登录部分 */
    {
        wxXmlNode* node = ReceiveXml(doc);
        userName = node->GetNodeContent();
        if(!loger->Login(userName)) throw ServerException(loginFail);
        SendLogin(botName);
        printf("Client \"%s\" (%s:%d) has connected!! \n",
               userName.mb_str(wxConvLocal).data(),
               addr.IPAddress().mb_str(wxConvLocal).data(),(int)addr.Service());
        SendMsg(kernel->respond("welcome message",userName.utf8_str().data()));
    }
    catch(const ServerException& e)
    {
        puts("---------------------------------------------------");
        puts("An exception occur when client login!!");
        switch(e)
        {
        case XmlParseFail:
            printf("Receive wrong xml message!  ");
            puts("The client connecting may be a wrong version!");
            break;
        case loginFail:
            SendExit(userName + wxT(" has login"));
            printf("Client (%s:%d) login fail with name \"%s\"\n",
                   addr.IPAddress().mb_str(wxConvLocal).data(),(int)addr.Service(),
                   userName.mb_str(wxConvLocal).data());
            userName = wxT("");
            break;
        case SocketError:
            puts("SocketError!!  Please check the internet!!");
            break;
        }
        puts("The thread is going to exit!!");
        puts("--------------------------------------------------------");
        Exit();
    }

    /**< 开始交互部分 */
    while(true)
    {
        try
        {
            wxXmlNode* node = ReceiveXml(doc);
            while(node)
            {
                if(node->GetName()==wxT("Message")) OnMessage(node->GetChildren());
                else if(node->GetName()==wxT("Respond")) OnRespond(node->GetChildren());
                node = node->GetNext();
            }
        }
        catch(const ServerException& e)
        {
        	switch(e)
        	{
			case XmlParseFail:
				printf("Receive wrong xml message from \"%s\"!\n",
						userName.mb_str(wxConvLocal).data());
				puts("The internet may has something wrong");
				break;
			case SocketError:
				printf("Client \"%s\" (%s:%d) has disconnected!! \n",
				   userName.mb_str(wxConvLocal).data(),
				   addr.IPAddress().mb_str(wxConvLocal).data(),(int)addr.Service());
				Exit();
        	}
        }
    }
    return 0;
}

void LinkThread::SendSetting()
{
    pm->SendQueryType(sock,timeStamp);
    timeStamp = time(0);
}

void LinkThread::SendString(const wxString& msg)
{
    Mysocket::SendString(sock,msg);
}
void LinkThread::SendLogin(const wxString& msg)
{
    SendString(wxT("<Login>")+msg+wxT("</Login>"));
}

void LinkThread::SendExit(const wxString& msg)
{
    SendString(wxT("<Exit>")+msg+wxT("</Exit>"));
}

void LinkThread::SendRespond(const wxString& msg)
{
    SendString(wxT("<Respond>\n<Query>")+msg+wxT("</Query>\n</Respond>"));
}
void LinkThread::SendMsg(const wxString& msg)
{
    wxString ftMg(msg);
    ftMg.Replace(wxT("\\n"),wxT("\n"));
    wxString ans = wxT("<Message>\n");
    ans += wxT("<Content>")+ ftMg + wxT("</Content>\n");
    ans += wxT("</Message>");
    SendString(ans);
    printf("%s> %s\n",botName.mb_str(wxConvLocal).data()
           ,msg.mb_str(wxConvLocal).data());
}
void LinkThread::SendMsg(const std::string& msg)
{
    SendMsg(wxString(msg.c_str(),wxConvUTF8));
}

void LinkThread::ReceiveString(wxString& msg)
{
    //  printf("Receiving!\n");
    Mysocket::ReceiveString(sock,msg);
}
wxXmlNode* LinkThread::ReceiveXml(wxXmlDocument& doc)
{
    wxXmlNode* root = doc.DetachRoot();
    if(root) delete(root);
    wxString msg;
    ReceiveString(msg);
    // printf("Receive---------------\n%s\n---------------------\n",
    //       msg.mb_str(wxConvLocal).data());
    wxStringInputStream rev(msg);
    if(!doc.Load(rev)) throw ServerException(XmlParseFail);
    return doc.GetRoot();
}


void LinkThread::WriteLog(const wxString& msg)
{
	wxTextFile file(wxT("log/")+userName+wxT(".txt"));
	if(!file.Open())
	{
		file.Create();
	}
	file.AddLine(wxDateTime::Now().Format());
	file.AddLine(msg);
	file.AddLine(wxT("------------------------------------------"));
	file.Write();
}

void LinkThread::OnMessage(wxXmlNode* node)
{
    wxString ask;
    while(node)
    {
        //printf("onMessage node: %s\n",node->GetName().c_str());
        if(node->GetName()==wxT("Content")&&node->GetChildren()) ask = node->GetNodeContent();
        node = node->GetNext();
    }
    if(ask.empty()) return;
    ask = FormatString::Split(ask);
    printf("%s> %s\n",userName.mb_str(wxConvLocal).data(),ask.mb_str(wxConvLocal).data());
    std::string stdStr = ask.utf8_str().data();
    stdStr = kernel->respond(stdStr,userName.utf8_str().data());
    if(stdStr.empty())
    {
        printf("No response! Require for a response!\n");
        WriteLog(ask);
        SendSetting();
        FormatString::DelPunc(ask);
        SendRespond(ask);
    }
    else
    {
        SendMsg(stdStr);
    }
}

void LinkThread::OnRespond(wxXmlNode* node)
{
    SendMsg(pm->AddCategory(node,userName));
}
